﻿#include "YImageDrawer.h"
#include <QVector>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QGraphicsProxyWidget>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSceneMouseEvent>
#include <QMouseEvent>
#include <QTimer>
#include <QtMath>
#include <QPlainTextEdit>
#include <QStyle>
#include <QScrollBar>
#include "DrawTools.h"
#include "Yuan.h"

#include <QDebug>
#define DP qDebug()
#define DF DP << __func__

namespace Y_Image_Drawer_GUI {
class YGraphicsChangeItem;
class YGraphicsItem{
public:
    virtual ~YGraphicsItem(){}
    virtual void rerect(const QRect& rect) {Q_UNUSED(rect);}
    virtual void rerect(const QPoint& pos){
        if(lastPos == pos) return;
        lastPos = pos;
        QRect rect;
        rect.setX(qMin(startPos.x(), pos.x()));
        rect.setY(qMin(startPos.y(), pos.y()));
        rect.setRight(qMax(startPos.x(), pos.x()));
        rect.setBottom(qMax(startPos.y(), pos.y()));
        rerect(rect);
    }
    virtual void reChangePos(){}
    virtual void showChange(){}
    virtual void hideChange(){}
    virtual void cursorChange(const QPointF&, YGraphicsChangeItem*){}
    bool inChangeHover();
public:
    YImageDrawer::Type itemType;
    QPoint startPos;
    QPoint lastPos;
    QVector<QPointF> changePos;
    QList<YGraphicsChangeItem*> changeItem;
    bool showChanged = false;
    bool inChangeArea = false;
    char AlignPadding[6];
    QPointF pressPos;
    int resizeType = 0;//0:移动,1:垂直水平,2:垂直,3:水平
    char AlignPadding2[4];
    YGraphicsChangeItem* cursorItem = nullptr;
    bool activeWindowFocus = false;
};
class YGraphicsChangeItem: public QGraphicsEllipseItem{
public:
    YGraphicsChangeItem():QGraphicsEllipseItem(QRect(-3, -3, 6, 6)){setBrush(Qt::white);setAcceptHoverEvents(true);}
    bool isHover = false;
    char AlignPadding[7];
    YGraphicsItem* item;
    QPointF pressPos;
    void hoverEnterEvent(QGraphicsSceneHoverEvent *){isHover = true;scene()->setProperty("inChangeArea", true);}
    void hoverLeaveEvent(QGraphicsSceneHoverEvent *){isHover = false;scene()->setProperty("inChangeArea", false);}
    void mousePressEvent(QGraphicsSceneMouseEvent *event){item->cursorChange(event->pos() + pos(), this);}
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event){item->cursorChange(event->pos() + pos(), this);}
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *){item->cursorChange(QPointF(), nullptr);}
};
bool YGraphicsItem::inChangeHover(){
    for(YGraphicsChangeItem* item : changeItem){
        if(item->isHover) return true;
    }
    return false;
}
class YGraphicsRectItem : public QGraphicsRectItem, public YGraphicsItem
{
public:
    void rerect(const QRect& rect){
        if(changePos.empty()){
            changePos.resize(8);
        }
        setPos(rect.topLeft());
        setRect(QRectF(0, 0, rect.width(), rect.height()));
    }
    void reChangePos(){
        QRectF rect(pos(), this->rect().size());
        changePos[0] = rect.topLeft();
        changePos[1] = rect.topRight();
        changePos[2] = rect.bottomLeft();
        changePos[3] = rect.bottomRight();
        changePos[4] = QPointF(rect.x() + rect.width() / 2.0, rect.y());
        changePos[5] = QPointF(rect.x() + rect.width() / 2.0, rect.bottom());
        changePos[6] = QPointF(rect.x(), rect.y() + rect.height() / 2.0);
        changePos[7] = QPointF(rect.right(), rect.y() + rect.height() / 2.0);
    }
    void showChange(){
        static QCursor cursors[8] = {Qt::SizeFDiagCursor, Qt::SizeBDiagCursor, Qt::SizeBDiagCursor, Qt::SizeFDiagCursor,
                                     Qt::SizeVerCursor, Qt::SizeVerCursor, Qt::SizeHorCursor, Qt::SizeHorCursor};
        if(changeItem.empty()){
            for(int i = 0; i < changePos.size(); ++i){
                YGraphicsChangeItem* ellipse = new YGraphicsChangeItem;
                changeItem.append(ellipse);
                ellipse->setCursor(cursors[i]);
                ellipse->item = this;
                scene()->addItem(ellipse);
            }
        }
        reChangePos();
        for(int i = 0; i < changePos.size(); ++i){
            changeItem[i]->setPos(changePos.at(i));
            QPen pen = this->pen();
            pen.setWidth(1);
            changeItem[i]->setPen(pen);
            changeItem[i]->show();
        }
        showChanged = true;
    }
    void hideChange(){
        for(int i = 0; i < changePos.size(); ++i){
            changeItem[i]->hide();
        }
        showChanged = false;
    }
    void cursorChange(const QPointF& cursorPressPos, YGraphicsChangeItem* cursorItem){
        if(this->cursorItem){
            if(cursorItem == this->cursorItem){
                QPoint mapCursorPressPos = cursorPressPos.toPoint();
                if(resizeType == 1){
                    YGraphicsItem::rerect(mapCursorPressPos);
                }else if(resizeType == 2){
                    if(lastPos.y() == mapCursorPressPos.y()) return;
                    lastPos = mapCursorPressPos;
                    QRect rect;
                    rect.setX(qMin(pos().toPoint().x(), startPos.x()));
                    rect.setY(qMin(startPos.y(), mapCursorPressPos.y()));
                    rect.setRight(qMax(pos().toPoint().x(), startPos.x()));
                    rect.setBottom(qMax(startPos.y(), mapCursorPressPos.y()));
                    rerect(rect);
                }else{
                    if(lastPos.x() == mapCursorPressPos.x()) return;
                    lastPos = mapCursorPressPos;
                    QRect rect;
                    rect.setX(qMin(startPos.x(), mapCursorPressPos.x()));
                    rect.setY(qMin(pos().toPoint().y(), startPos.y()));
                    rect.setRight(qMax(startPos.x(), mapCursorPressPos.x()));
                    rect.setBottom(qMax(pos().toPoint().y(), startPos.y()));
                    rerect(rect);
                }
                showChange();
            }else{
                resizeType = 0;
                this->cursorItem = nullptr;
                setFocus();
            }
        }else{
            this->cursorItem = cursorItem;
            int index = changeItem.indexOf(cursorItem);
            if(index < 4) resizeType = 1;
            else if(index < 6) resizeType = 2;
            else resizeType = 3;
            QRect rect = QRectF(pos(), this->rect().size()).toRect();
            switch(index){
            case 0: startPos = rect.bottomRight();break;
            case 1: startPos = rect.bottomLeft();break;
            case 2: startPos = rect.topRight();break;
            case 3: startPos = rect.topLeft();break;
            case 4: startPos = rect.bottomRight();break;
            case 5: startPos = rect.topRight();break;
            case 6: startPos = rect.bottomRight();break;
            case 7: startPos = rect.bottomLeft();break;
            }
        }
    }
    explicit YGraphicsRectItem(const QPointF &pos, const QSizeF& size): QGraphicsRectItem(QRectF(QPointF(0, 0), size)){
        itemType = YImageDrawer::Rect;
        setPos(pos);
        setAcceptHoverEvents(true);
        setBrush(Qt::NoBrush);
        setFlag(QGraphicsItem::ItemIsFocusable);
    }
    virtual void focusInEvent(QFocusEvent *){
        showChange();
    }
    virtual void focusOutEvent(QFocusEvent *){
        if(!inChangeHover()){
            hideChange();
        }
    }
    virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event){
        QPointF epos = event->pos();
        bool xValid = ((epos.x() >= rect().x() && epos.x() <= rect().x() + pen().width()) ||
                       (epos.x() <= rect().right() && epos.x() >= rect().right() - pen().width()));
        bool yValid = ((epos.y() >= rect().y() && epos.y() <= rect().y() + pen().width()) ||
                       (epos.y() <= rect().bottom() && epos.y() >= rect().bottom() - pen().width()));
        inChangeArea = (xValid || yValid);
        scene()->setProperty("inChangeArea", inChangeArea);
        if(inChangeArea){
            setCursor(Qt::SizeAllCursor);
        }else{
            unsetCursor();
        }
    }
    virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = false;
        scene()->setProperty("inChangeArea", inChangeArea);
        unsetCursor();
    }
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event){
        pressPos = event->pos();
        hideChange();
    }
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
        if(inChangeArea){
            if(resizeType == 0){
                QPointF pos = this->pos() - pressPos + event->pos();
                setPos(pos);
            }
        }
    }
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *){if(inChangeArea){reChangePos();showChange();}}
};
class YGraphicsEllipseItem : public QGraphicsEllipseItem, public YGraphicsItem
{
public:
    void rerect(const QRect& rect){
        if(changePos.empty()){
            changePos.resize(4);
        }
        setPos(rect.topLeft());
        setRect(QRectF(0, 0, rect.width(), rect.height()));
    }
    void reChangePos(){
        QRectF rect(pos(), this->rect().size());
        changePos[0] = QPointF(rect.x() + rect.width() / 2.0, rect.y());
        changePos[1] = QPointF(rect.x() + rect.width() / 2.0, rect.bottom());
        changePos[2] = QPointF(rect.x(), rect.y() + rect.height() / 2.0);
        changePos[3] = QPointF(rect.right(), rect.y() + rect.height() / 2.0);
    }
    void showChange(){
        static QCursor cursors[4] = {Qt::SizeVerCursor, Qt::SizeVerCursor, Qt::SizeHorCursor, Qt::SizeHorCursor};
        if(changeItem.empty()){
            for(int i = 0; i < changePos.size(); ++i){
                YGraphicsChangeItem* ellipse = new YGraphicsChangeItem;
                changeItem.append(ellipse);
                ellipse->setCursor(cursors[i]);
                ellipse->item = this;
                scene()->addItem(ellipse);
            }
        }
        reChangePos();
        for(int i = 0; i < changePos.size(); ++i){
            changeItem[i]->setPos(changePos.at(i));
            QPen pen = this->pen();
            pen.setWidth(1);
            changeItem[i]->setPen(pen);
            changeItem[i]->show();
        }
        showChanged = true;
    }
    void hideChange(){
        for(int i = 0; i < changePos.size(); ++i){
            changeItem[i]->hide();
        }
        showChanged = false;
    }
    void cursorChange(const QPointF& cursorPressPos, YGraphicsChangeItem* cursorItem){
        if(this->cursorItem){
            if(cursorItem == this->cursorItem){
                QPoint mapCursorPressPos = cursorPressPos.toPoint();
                if(resizeType == 1){
                    YGraphicsItem::rerect(mapCursorPressPos);
                }else if(resizeType == 2){
                    if(lastPos.y() == mapCursorPressPos.y()) return;
                    lastPos = mapCursorPressPos;
                    QRect rect;
                    rect.setX(qMin(pos().toPoint().x(), startPos.x()));
                    rect.setY(qMin(startPos.y(), mapCursorPressPos.y()));
                    rect.setRight(qMax(pos().toPoint().x(), startPos.x()));
                    rect.setBottom(qMax(startPos.y(), mapCursorPressPos.y()));
                    rerect(rect);
                }else{
                    if(lastPos.x() == mapCursorPressPos.x()) return;
                    lastPos = mapCursorPressPos;
                    QRect rect;
                    rect.setX(qMin(startPos.x(), mapCursorPressPos.x()));
                    rect.setY(qMin(pos().toPoint().y(), startPos.y()));
                    rect.setRight(qMax(startPos.x(), mapCursorPressPos.x()));
                    rect.setBottom(qMax(pos().toPoint().y(), startPos.y()));
                    rerect(rect);
                }
                showChange();
            }else{
                resizeType = 0;
                this->cursorItem = nullptr;
                setFocus();
            }
        }else{
            this->cursorItem = cursorItem;
            int index = changeItem.indexOf(cursorItem);
            if(index < 2) resizeType = 2;
            else resizeType = 3;
            QRect rect = QRectF(pos(), this->rect().size()).toRect();
            switch(index){
            case 0: startPos = rect.bottomRight();break;
            case 1: startPos = rect.topRight();break;
            case 2: startPos = rect.bottomRight();break;
            case 3: startPos = rect.bottomLeft();break;
            }
        }
    }
    explicit YGraphicsEllipseItem(const QPointF &pos, const QSizeF& size): QGraphicsEllipseItem(QRectF(QPointF(0, 0), size)){
        itemType = YImageDrawer::Ellipse;
        setPos(pos);
        setAcceptHoverEvents(true);
        setBrush(Qt::NoBrush);
        setFlag(QGraphicsItem::ItemIsFocusable);
    }
    virtual void focusInEvent(QFocusEvent *){
        showChange();
    }
    virtual void focusOutEvent(QFocusEvent *){
        if(!inChangeHover()){
            hideChange();
        }
    }
    virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event){
        QPointF epos = event->pos();
        QPainterPath path = shape();
        inChangeArea = (path.contains(epos) &&
                        !path.contains(QRectF(epos.x() - pen().width(), epos.y() - pen().width(),
                                              pen().width() + 1, pen().width() + 1)));
        scene()->setProperty("inChangeArea", inChangeArea);
        if(inChangeArea){
            setCursor(Qt::SizeAllCursor);
        }else{
            unsetCursor();
        }
    }
    virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = false;
        scene()->setProperty("inChangeArea", inChangeArea);
        unsetCursor();
    }
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event){
        pressPos = event->pos();
        hideChange();
    }
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
        if(inChangeArea){
            if(resizeType == 0){
                QPointF pos = this->pos() - pressPos + event->pos();
                setPos(pos);
            }
        }
    }
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *){if(inChangeArea){reChangePos();showChange();}}
};
class YGraphicsArrowItem : public QGraphicsPolygonItem, public YGraphicsItem
{
public:
    virtual void rerect(const QPoint& pos){
        if(lastPos == pos) return;
        lastPos = pos;
        makePath();
    }
    QPoint rotatePoint(const QPoint& pos, qreal cosTheta, qreal sinTheta, int positiveX, int positiveY)
    {
        return QPoint(qRound(pos.x() * cosTheta + pos.y() * sinTheta) * positiveX,
                      qRound(-pos.x() * sinTheta + pos.y() * cosTheta) * positiveY);
    }
    char AlignPadding_YGraphicsArrowItem[7];
    QVector<QPoint> points;
    void makePath(){
        QPoint pos = this->pos().toPoint();
        int x = qAbs(lastPos.x() - pos.x()), y = qAbs(lastPos.y() - pos.y());
        qreal rwidth = qSqrt(x*x+y*y), theta = qAcos(x / rwidth);
        int width = qRound(rwidth);
        int grade = pen().width();
        qreal cosTheta = qCos(theta);
        qreal sinTheta = qSin(theta);
        int positiveX = (lastPos.x() >= pos.x() ? 1 : -1), positiveY = (lastPos.y() <= pos.y() ? 1 : -1);

        int size = 11;
        if(width >= 30 && grade > 1) size = 15;
        if(width >= 40 && grade > 2) size = 21;

        if(points.size() != 6) points.resize(6);
        points[3] = rotatePoint(QPoint(width, 0), cosTheta, sinTheta, positiveX, positiveY);

        int wp = width - size, ah = size / 2, wh = ah / 2;
        points[1] = rotatePoint(QPoint(wp, -wh), cosTheta, sinTheta, positiveX, positiveY);
        points[2] = rotatePoint(QPoint(wp, -ah), cosTheta, sinTheta, positiveX, positiveY);
        points[4] = rotatePoint(QPoint(wp, ah), cosTheta, sinTheta, positiveX, positiveY);
        points[5] = rotatePoint(QPoint(wp, wh), cosTheta, sinTheta, positiveX, positiveY);
        setPolygon(QPolygon(points));
    }
    void reChangePos(){
        changePos[0] = pos();
        changePos[1] = QPointF(lastPos);
    }
    void showChange(){
        static QCursor cursors[2] = {Qt::SizeFDiagCursor, Qt::SizeBDiagCursor};
        if(changePos.empty()){
            changePos.resize(2);
        }
        if(changeItem.empty()){
            for(int i = 0; i < changePos.size(); ++i){
                YGraphicsChangeItem* ellipse = new YGraphicsChangeItem;
                changeItem.append(ellipse);
                ellipse->setCursor(cursors[i]);
                ellipse->item = this;
                scene()->addItem(ellipse);
            }
        }
        reChangePos();
        for(int i = 0; i < changePos.size(); ++i){
            changeItem[i]->setPos(changePos.at(i));
            changeItem[i]->setPen(QPen(brush().color(), 1));
            changeItem[i]->show();
        }
        showChanged = true;
    }
    void hideChange(){
        for(int i = 0; i < changePos.size(); ++i){
            changeItem[i]->hide();
        }
        showChanged = false;
    }
    void cursorChange(const QPointF& cursorPressPos, YGraphicsChangeItem* cursorItem){
        if(this->cursorItem){
            if(cursorItem == this->cursorItem){
                QPoint mapCursorPressPos = cursorPressPos.toPoint();
                if(resizeType == 1){
                    if(cursorPressPos == pos()) return;
                    setPos(cursorPressPos);
                    makePath();
                }else{
                    if(mapCursorPressPos == lastPos) return;
                    lastPos = mapCursorPressPos;
                    makePath();
                }
                showChange();
            }else{
                resizeType = 0;
                this->cursorItem = nullptr;
                setFocus();
            }
        }else{
            this->cursorItem = cursorItem;
            resizeType = changeItem.indexOf(cursorItem) + 1;
        }
    }
    explicit YGraphicsArrowItem(const QPointF &pos, const QSize&): QGraphicsPolygonItem(){
        itemType = YImageDrawer::Arrow;
        setPos(pos);
        rerect(startPos);
        setAcceptHoverEvents(true);
        points.resize(6);
        points[0] = QPoint(0, 0);
        setFlag(QGraphicsItem::ItemIsFocusable);
    }
    virtual void focusInEvent(QFocusEvent *){
        showChange();
    }
    virtual void focusOutEvent(QFocusEvent *){
        if(!inChangeHover()){
            hideChange();
        }
    }
    virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = true;
        scene()->setProperty("inChangeArea", inChangeArea);
        setCursor(Qt::SizeAllCursor);
    }
    virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = false;
        scene()->setProperty("inChangeArea", inChangeArea);
        unsetCursor();
    }
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event){
        pressPos = event->pos();
        hideChange();
    }
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
        if(inChangeArea){
            if(resizeType == 0){
                QPointF pos = this->pos() - pressPos + event->pos();
                lastPos =(QPointF(lastPos) - pressPos + event->pos()).toPoint();
                setPos(pos);
                makePath();
            }
        }
    }
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *){if(inChangeArea){showChange();}}
};
class YGraphicsPenItem : public QGraphicsPathItem, public YGraphicsItem
{
public:
    virtual void rerect(const QPoint& pos){
        if(lastPos == pos) return;
        lastPos = pos;
        points.append(pos - startPos);
        makePath();
    }
    char AlignPadding_YGraphicsArrowItem[7];
    QList<QPoint> points;
    void makePath(){
        QPainterPath path;
        path.moveTo(0, 0);
        for(const QPoint& p : points){
            path.lineTo(p);
        }
        setPath(path);
    }
    explicit YGraphicsPenItem(const QPointF &pos, const QSize&): QGraphicsPathItem(){
        itemType = YImageDrawer::Pen;
        setPos(pos);
        setAcceptHoverEvents(true);
        setFlag(QGraphicsItem::ItemIsFocusable);
    }
    virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = true;
        scene()->setProperty("inChangeArea", inChangeArea);
        setCursor(Qt::SizeAllCursor);
    }
    virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = false;
        scene()->setProperty("inChangeArea", inChangeArea);
        unsetCursor();
    }
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event){
        pressPos = event->pos();
        hideChange();
    }
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
        if(inChangeArea){
            if(resizeType == 0){
                QPointF pos = this->pos() - pressPos + event->pos();
                setPos(pos);
            }
        }
    }
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *){if(inChangeArea){showChange();}}
};
class YGraphicsMosaicItem : public QGraphicsItem, public YGraphicsItem
{
public:
    explicit YGraphicsMosaicItem(QImage image):
            QGraphicsItem(), basicImage(image){
        itemType = YImageDrawer::Mosaic;
        mosaicImage = basicImage.copy();
        map.resize(image.width());
        for(QVector<bool>& maps : map){
            maps.resize(image.height());
            for(bool& b : maps){
                b = false;
            }
        }
    }
    void setVague(int value){
        vague = int(0.3 * value);
        if(vague < 3) vague = 3;
        makeMosaic();
        update();
    }
    inline void setThickness(int value) { thickness = value; }
    virtual void rerect(const QPoint& pos){
        if(lastPos == pos) return;
        lastPos = pos;
        int radius = (thickness == 1 ? 9 : (thickness == 2 ? 19 : 29));
        QRect rect(pos.x() - radius, pos.y() - radius, radius, radius);
        if(rect.x() < 0) rect.setX(0);
        if(rect.y() < 0) rect.setY(0);
        if(rect.right() > basicImage.width()) rect.setRight(basicImage.width());
        if(rect.bottom() > basicImage.height()) rect.setBottom(basicImage.height());
        bool changed = false;
        for(int row = rect.x(); row < rect.right(); ++row){
            for(int col = rect.y(); col < rect.bottom(); ++col){
                if(!map[row][col]){
                    changed = true;
                    map[row][col] = true;
                }
            }
        }
        if(changed) update();
    }
    void makeMosaic(){
        for(int row = 0; row < basicImage.width(); ++row){
            int rr = int(row / vague) * vague;
            for(int col = 0; col < basicImage.height(); ++col){
                int cc = int(col / vague) * vague;
                mosaicImage.setPixel(row, col, basicImage.pixel(rr, cc));
            }
        }
    }
    virtual QRectF boundingRect() const{ return basicImage.rect(); }
    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*){
//        painter->drawImage()
        for(int row = 0; row < map.size(); ++row){
            for(int col = 0; col < basicImage.height(); ++col){
                if(map[row][col]){
                    painter->setPen(mosaicImage.pixelColor(row, col));
                    painter->drawPoint(row, col);
                }
            }
        }
    }
    QImage basicImage, mosaicImage;
    QVector<QVector<bool> > map;
    int thickness, vague;
};
static inline int boundingWidth(QFontMetrics& fm, const QString& content){
#if (QT_VERSION >= QT_VERSION_CHECK(5,11,0))
        return fm.horizontalAdvance(content);
#else
        return fm.width(content);
#endif
}
class YGraphicsTextItem : public QGraphicsProxyWidget, public YGraphicsItem
{
public:
    void resize(const QString& content = "占位"){
        QFontMetrics fm(edit->font());
        int width = boundingWidth(fm, "占位"), height = 4;
        for(const QString& line : content.split('\n')){
            int tw = boundingWidth(fm, line + "占");
            if(tw > width) width = tw;
            height += fm.lineSpacing();
        }

        QGraphicsWidget::resize(width, height);
        edit->resize(width, height);
        setMinimumHeight(height);
        setMaximumHeight(height);
        update();
    }

    YGraphicsTextItem(): QGraphicsProxyWidget(){
        itemType = YImageDrawer::Text;
        setAcceptHoverEvents(true);
        setFlag(QGraphicsItem::ItemIsFocusable);

        edit = new QPlainTextEdit;
        edit->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
        edit->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
        edit->setContextMenuPolicy(Qt::NoContextMenu);
        setWidget(edit);
        edit->installEventFilter(this);
        edit->connect(edit, &QPlainTextEdit::textChanged, [this]{resize(edit->toPlainText());});
    }

    bool eventFilter(QObject* object, QEvent* event){
        if(event->type() == QEvent::FocusIn){
            scene()->setProperty("OccupyFocus", true);
        }else if(event->type() == QEvent::FocusOut){
            scene()->setProperty("OccupyFocus", false);
            if(edit->toPlainText().isEmpty() && !activeWindowFocus){
                emit edit->customContextMenuRequested(QPoint());
            }else{
                update();
            }
        }
        return QObject::eventFilter(object, event);
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
        QGraphicsProxyWidget::paint(painter, option, widget);
        if(hasFocus() || edit->hasFocus() || activeWindowFocus){
            painter->setPen(QPen(Qt::red, 2));
            painter->drawRect(rect());
        }
    }
    virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event){
        QPointF epos = event->pos();
        bool xValid = ((epos.x() >= rect().x() && epos.x() <= rect().x() + 2) ||
                       (epos.x() <= rect().right() && epos.x() >= rect().right() - 2));
        bool yValid = ((epos.y() >= rect().y() && epos.y() <= rect().y() + 2) ||
                       (epos.y() <= rect().bottom() && epos.y() >= rect().bottom() - 2));
        inChangeArea = (xValid || yValid);
        scene()->setProperty("inChangeArea", true);
        if(inChangeArea){
            setCursor(Qt::SizeAllCursor);
        }else{
            setCursor(Qt::IBeamCursor);
        }
    }
    virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = false;
        scene()->setProperty("inChangeArea", false);
        unsetCursor();
    }
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event){
        pressPos = event->pos();
    }
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
        if(inChangeArea){
            if(resizeType == 0){
                QPointF pos = this->pos() - pressPos + event->pos();
                setPos(pos);
            }
        }
    }
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent){
        edit->setFocus();
        edit->setTextCursor(edit->cursorForPosition(mouseEvent->pos().toPoint()));
    }
public:
    char AlignPadding_YGraphicsArrowItem[7];
    QPlainTextEdit* edit;
};
class YGraphicsTipItemPanel : public QFrame
{
public:
    YGraphicsTipItemPanel(){
        setWindowFlags(Qt::FramelessWindowHint);
        setAttribute(Qt::WA_TranslucentBackground);
        ++count;
        self = count;
    }
    ~YGraphicsTipItemPanel(){
        --count;
    }
    void paintEvent(QPaintEvent*){
        QRectF rect(0, 5, 20, 20);
        QPainter painter(this);
        painter.setPen(Qt::NoPen);
        painter.setBrush(color);
        painter.drawEllipse(rect);
        painter.setBrush(QColor(0,0,0,120));
        static QPoint points[3] = {QPoint(30, 10), QPoint(30, 20), QPoint(24, 15)};
        painter.drawPolygon(points, 3);
        painter.setPen((color == Qt::white ? Qt::black : Qt::white));
        painter.setBrush(Qt::NoBrush);
        painter.drawText(rect, Qt::AlignCenter, QString::number(self));
        painter.setPen(Qt::NoPen);
        painter.fillRect(this->rect(), QColor(0,0,0,0));
    }
    QColor color = Qt::red;
    static int count;
    int self;
};
int YGraphicsTipItemPanel::count = 0;

class YGraphicsTipItem : public QGraphicsProxyWidget, public YGraphicsItem
{
public:
    void resize(const QString& content = "占位"){
        QFontMetrics fm(edit->font());
        int width = boundingWidth(fm, "占位"), height = 4;
        for(const QString& line : content.split('\n')){
            int tw = boundingWidth(fm, line + "占");
            if(tw > width) width = tw;
            height += fm.lineSpacing();
        }

        QGraphicsWidget::resize(width + 30, height);
        panel->resize(width + 30, height);
        edit->setGeometry(30, 0, width, height);
        edit->show();
        setMinimumHeight(height);
        setMaximumHeight(height);
        update();
    }

    YGraphicsTipItem(): QGraphicsProxyWidget(){
        itemType = YImageDrawer::Tip;
        setAcceptHoverEvents(true);
        setFlag(QGraphicsItem::ItemIsFocusable);

        panel = new YGraphicsTipItemPanel;
        setWidget(panel);

        edit = new QPlainTextEdit(panel);
        edit->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
        edit->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
        edit->setContextMenuPolicy(Qt::NoContextMenu);
        edit->installEventFilter(this);
        edit->setStyleSheet(QString("color:white;background-color:rgba(0,0,0,120);border-radius:5px;border:none;"));
        edit->connect(edit, &QPlainTextEdit::textChanged, [this]{resize(edit->toPlainText());});
    }

    bool eventFilter(QObject* object, QEvent* event){
        if(event->type() == QEvent::FocusIn){
            scene()->setProperty("OccupyFocus", true);
        }else if(event->type() == QEvent::FocusOut){
            scene()->setProperty("OccupyFocus", false);
            if(edit->toPlainText().isEmpty() && !activeWindowFocus){
                emit edit->customContextMenuRequested(QPoint());
            }else{
                update();
            }
        }
        return QObject::eventFilter(object, event);
    }
    virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event){
        QPointF epos = event->pos();
        inChangeArea = (epos.x() < 20 && epos.y() < 20);
        scene()->setProperty("inChangeArea", true);
        if(inChangeArea){
            setCursor(Qt::SizeAllCursor);
        }else{
            setCursor(Qt::IBeamCursor);
        }
    }
    virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *){
        inChangeArea = false;
        scene()->setProperty("inChangeArea", false);
        unsetCursor();
    }
    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event){
        pressPos = event->pos();
    }
    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
        if(inChangeArea){
            if(resizeType == 0){
                QPointF pos = this->pos() - pressPos + event->pos();
                setPos(pos);
            }
        }
    }
    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent){
        if(!inChangeArea){
            edit->setFocus();
            edit->setTextCursor(edit->cursorForPosition(mouseEvent->pos().toPoint()));
        }
    }
public:
    char AlignPadding_YGraphicsArrowItem[7];
    QPlainTextEdit* edit;
    YGraphicsTipItemPanel* panel;
};
};
using namespace Y_Image_Drawer_GUI;

QCursor getMosaicCursor(int thickness){
    static QCursor min = QCursor(QPixmap(":/min.png"), 9, 9);
    static QCursor middle = QCursor(QPixmap(":/middle.png"), 19, 19);
    static QCursor max = QCursor(QPixmap(":/max.png"), 29, 29);
    return (thickness == 1 ? min : (thickness == 2 ? middle : max));
}

YImageDrawer::YImageDrawer(const QImage& image, QWidget *parent) : QWidget(parent), image(image)
{
    setFixedSize(image.size());
    scene = new QGraphicsScene(image.rect(), this);
    view = new QGraphicsView(scene, this);
    view->setStyleSheet("border: none;");

    view->setRenderHint(QPainter::Antialiasing);
    view->viewport()->installEventFilter(this);
    view->resize(image.size());
    scene->addPixmap(QPixmap::fromImage(image));
    scene->setProperty("inChangeArea", false);
    scene->setSceneRect(view->rect());

    connect(scene, &QGraphicsScene::focusItemChanged,
            [this](QGraphicsItem *, QGraphicsItem *oldFocus, Qt::FocusReason reason){
        if(oldFocus && reason == Qt::ActiveWindowFocusReason){
            if(scene->focusItem()){
                focusItem = dynamic_cast<YGraphicsItem*>(scene->focusItem());
                focusItem->activeWindowFocus = true;
            }
        }else if(focusItem){
            focusItem->activeWindowFocus = false;
            focusItem = nullptr;
        }
    });

    mosaic = new YGraphicsMosaicItem(image);
    scene->addItem(mosaic);
}

void YImageDrawer::setType(int t)
{
    type = Type(t);
    if(type == Mosaic){
        view->setCursor(getMosaicCursor(tools->getThickness()));
    }else{
        setViewCursor(Yuan::CustomCursor());
    }
}

void YImageDrawer::setViewCursor(const QCursor &cursor)
{
    static QCursor staticCursor = cursor;
    view->setCursor(staticCursor);
}

void YImageDrawer::setTools(DrawTools *t)
{
    tools = t;
    mosaic->setThickness(tools->getThickness());
    mosaic->setVague(tools->getVague());
}

void YImageDrawer::thicknessChanged(int thickness)
{
    QAbstractGraphicsShapeItem* item = dynamic_cast<QAbstractGraphicsShapeItem*>(scene->focusItem());
    if(item){
        QPen pen = item->pen();
        pen.setWidth(thickness);
        item->setPen(pen);
        if(dynamic_cast<YGraphicsItem*>(scene->focusItem())->itemType == Arrow){
            dynamic_cast<YGraphicsArrowItem*>(scene->focusItem())->makePath();
        }
        item->update();
    }
    mosaic->setThickness(thickness);
    if(type == Mosaic){
        view->setCursor(getMosaicCursor(thickness));
    }
}

void YImageDrawer::colorChanged(QColor color)
{
    if(!scene->focusItem()) return;
    YGraphicsItem* yitem = dynamic_cast<YGraphicsItem*>(scene->focusItem());
    if(scene->focusItem()->isWidget()){
        if(yitem->itemType == Text){
            dynamic_cast<YGraphicsTextItem*>(yitem)->edit->setStyleSheet(QString("color:rgb(%1,%2,%3);background-color:rgba(0,0,0,0);border:none;")
                                      .arg(color.red()).arg(color.green()).arg(color.blue()));
            scene->focusItem()->update();
        }
        return;
    }
    QAbstractGraphicsShapeItem* item = dynamic_cast<QAbstractGraphicsShapeItem*>(scene->focusItem());
    if(item){
        if(yitem->itemType == Arrow){
            item->setBrush(color);
        }else{
            QPen pen = item->pen();
            pen.setColor(color);
            item->setPen(pen);
        }
        item->update();
    }
}

void YImageDrawer::fontChanged(const QFont &font)
{
    if(!scene->focusItem()) return;
    YGraphicsItem* yitem = dynamic_cast<YGraphicsItem*>(scene->focusItem());
    if(yitem){
        if(yitem->itemType == Text){
            YGraphicsTextItem* item = dynamic_cast<YGraphicsTextItem*>(scene->focusItem());
            item->edit->setFont(font);
            item->resize(item->edit->toPlainText());
        }else if(yitem->itemType == Tip){
            YGraphicsTipItem* item = dynamic_cast<YGraphicsTipItem*>(scene->focusItem());
            item->edit->setFont(font);
            item->resize(item->edit->toPlainText());
        }
    }
}

void YImageDrawer::vagueChanged(int vague)
{
    mosaic->setVague(vague);
}

void YImageDrawer::redo()
{
    if(items.isEmpty()) return;
    QGraphicsItem* qGraphicsItem = items.takeLast();
    scene->removeItem(qGraphicsItem);
    delete qGraphicsItem;
}

void YImageDrawer::createItem(const QPoint &pos)
{
    QGraphicsItem* qGraphicsItem = nullptr;
    switch(type){
    case Rect: {
        YGraphicsRectItem* item = new YGraphicsRectItem(pos, QSizeF(2, 2));
        qGraphicsItem = item;
        currentItem = item;
        item->setPen(QPen(tools->getColor(), tools->getThickness()));
    };break;
    case Ellipse: {
        YGraphicsEllipseItem* item = new YGraphicsEllipseItem(pos, QSize(2, 2));
        qGraphicsItem = item;
        currentItem = item;
        item->setPen(QPen(tools->getColor(), tools->getThickness()));
    };break;
    case Arrow: {
        YGraphicsArrowItem* item = new YGraphicsArrowItem(pos, QSize(2, 2));
        qGraphicsItem = item;
        currentItem = item;
        QPen pen = Qt::NoPen;
        pen.setWidth(tools->getThickness());
        item->setPen(pen);
        item->setBrush(tools->getColor());
    };break;
    case Pen: {
        YGraphicsPenItem* item = new YGraphicsPenItem(pos, QSize(2, 2));
        qGraphicsItem = item;
        currentItem = item;
        item->setPen(QPen(tools->getColor(), tools->getThickness()));
    };break;
    case Text: {
        YGraphicsTextItem* item = new YGraphicsTextItem;
        qGraphicsItem = item;
        currentItem = item;
        item->edit->setFont(tools->getFont());
        QColor color = tools->getColor();
        item->edit->setStyleSheet(QString("color:rgb(%1,%2,%3);background-color:rgba(0,0,0,0);border:none;")
                                  .arg(color.red()).arg(color.green()).arg(color.blue()));
        item->resize();
        item->setPos(pos);
        item->edit->setFocus();
        connect(item->edit, &QPlainTextEdit::customContextMenuRequested, [this, qGraphicsItem]{
            items.removeOne(qGraphicsItem);
            scene->removeItem(qGraphicsItem);
            QTimer::singleShot(1000, [qGraphicsItem]{
                if(qGraphicsItem) delete qGraphicsItem;
            });
        });
    };break;
    case Tip: {
        YGraphicsTipItem* item = new YGraphicsTipItem;
        qGraphicsItem = item;
        currentItem = item;
        item->setPos(pos);
        item->edit->setFont(tools->getFont());
        item->panel->color = tools->getColor();
        item->resize();
        item->setPos(pos);
        item->edit->setFocus();
    };break;
    default:return;
    }
    currentItem->startPos = pos;
    scene->addItem(qGraphicsItem);
    items.append(qGraphicsItem);
}

bool YImageDrawer::eventFilter(QObject *object, QEvent *event)
{
    QMouseEvent *mouseEvent = reinterpret_cast<QMouseEvent*>(event);
    if(event->type() == QEvent::MouseButtonPress){
        if(!scene->property("OccupyFocus").toBool()){
            pressed = true;
            startPos = mouseEvent->pos();
        }
    }else if(event->type() == QEvent::MouseMove){
        if(type < Text) {
            if(type == Mosaic && pressed){
                mosaic->rerect(mouseEvent->pos());
            }else if(pressed && !scene->property("inChangeArea").toBool()){
                if(!currentItem){
                    if(startPos != mouseEvent->pos())
                        createItem(startPos);
                }
                if(currentItem){
                    currentItem->rerect(mouseEvent->pos());
                }
            }
        }
    }else if(event->type() == QEvent::MouseButtonRelease){
        if(pressed && type >= Text && !scene->property("inChangeArea").toBool()){
            createItem(startPos);
        }
        pressed = false;
        currentItem = nullptr;
    }
    return QWidget::eventFilter(object, event);
}
